<html lang="en-US">

<head>
    
<title>我要在栈上。不，你应该在堆上 - 花落雨忧</title>

<meta property="og:title" content="我要在栈上。不，你应该在堆上 - 花落雨忧">



    



    
    <meta property="description" content="我们在写代码的时候，有时候会想这个变量到底分配到哪里了？这时候可能会有人说，在栈上，在堆上。信我准没错&amp;hellip;
但从结果上来讲你还是一知半解，这可不行，万一被人懵了呢。今天我们一起来深挖下 Go 在这块的奥妙，自己动手丰衣足食
[&amp;hellip;] type User struct { ID int64 Name string Avatar string } func &amp;hellip;">
    <meta property="og:description" content="我们在写代码的时候，有时候会想这个变量到底分配到哪里了？这时候可能会有人说，在栈上，在堆上。信我准没错&amp;hellip;
但从结果上来讲你还是一知半解，这可不行，万一被人懵了呢。今天我们一起来深挖下 Go 在这块的奥妙，自己动手丰衣足食
[&amp;hellip;] type User struct { ID int64 Name string Avatar string } func &amp;hellip;">
    






<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1">

<link rel="shortcut icon" href="https://www.lican.site/logo/logo.png" type="image/x-icon" />



<link rel="stylesheet" href="/css/style.min.css" />

<link rel="stylesheet" href="/css/reset.min.css" />




<script src="https://www.lican.site/js/highlight.min.js"></script>

<script>
hljs.configure({ ignoreUnescapedHTML: true })
hljs.highlightAll();
</script>


<script src="https://www.lican.site/js/jquery.min.js"></script>




<link href="https://www.lican.site/css/hugo-code.min.css" rel="stylesheet" />



    <style>
        .post-content img {
            max-width: 400px;
        }
    </style>
</head>

<body id="period" class="home blog">
    <a class="skip-content" href="#main">Press "Enter" to skip to content</a>
    <div id="overflow-container" class="overflow-container">
        <header class="site-header" id="site-header" role="banner">
    <div class="max-width">
        <div id="title-container" class="title-container">
            <div id="site-title" class="site-title"><a href="/">花落雨忧</a></div>
            <p class="tagline">思所及 力所行 方为真.</p>
        </div>
        
        <div id="menu-primary-container" class="menu-primary-container">
            <div class="icon-container">
            </div>
            <div id="menu-primary" class="menu-container menu-primary" role="navigation">
                <nav class="menu">
                    <ul id="menu-primary-items" class="menu-primary-items">
                        
                        
                        <li id="menu-item-0"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/posts" aria-current="page" tabindex="0">首页</a></li>
                        
                        <li id="menu-item-1"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/tech/" aria-current="page" tabindex="1">技术文档</a></li>
                        
                        <li id="menu-item-2"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/article/" aria-current="page" tabindex="2">文章</a></li>
                        
                        <li id="menu-item-3"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/project/" aria-current="page" tabindex="3">项目</a></li>
                        
                        <li id="menu-item-4"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/about/" aria-current="page" tabindex="4">关于</a></li>
                        
                    </ul>
                </nav>
            </div>
        </div>
    </div>
</header>
        <div id="primary-container" class="primary-container">
            <div class="max-width">
                <section id="main" class="main" role="main">
                    <div id="loop-container" class="loop-container">
                        <div
                            class="post type-post status-publish format-standard hentry entry">
                            <article>
                                <div class="post-container">
                                    <div class="post-header">
                                        <h2 class="post-title">
                                            <a href="/posts/go/talk/2019-05-20-stack-heap/">我要在栈上。不，你应该在堆上</a>
                                        </h2>
                                        
                                        <div class="post-byline">Published on
                                            <a class="date" href="javascript:;">2019/05/20</a>
                                            
                                            
                                            
                                            By 
                                            <a class="author" href="javascript:;">煎鱼</a></div>
                                            
                                        
                                    </div>
                                    <div class="post-content">
                                        <p><img src="https://s2.ax1x.com/2020/02/27/3wK39K.jpg" alt="image"></p>
<p>我们在写代码的时候，有时候会想这个变量到底分配到哪里了？这时候可能会有人说，在栈上，在堆上。信我准没错&hellip;</p>
<p>但从结果上来讲你还是一知半解，这可不行，万一被人懵了呢。今天我们一起来深挖下 Go 在这块的奥妙，自己动手丰衣足食</p>
<h2 id="问题">问题</h2>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">User</span> <span style="color:#66d9ef">struct</span> {
	<span style="color:#a6e22e">ID</span>     <span style="color:#66d9ef">int64</span>
	<span style="color:#a6e22e">Name</span>   <span style="color:#66d9ef">string</span>
	<span style="color:#a6e22e">Avatar</span> <span style="color:#66d9ef">string</span>
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GetUserInfo</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span> {
	<span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">User</span>{<span style="color:#a6e22e">ID</span>: <span style="color:#ae81ff">13746731</span>, <span style="color:#a6e22e">Name</span>: <span style="color:#e6db74">&#34;EDDYCJY&#34;</span>, <span style="color:#a6e22e">Avatar</span>: <span style="color:#e6db74">&#34;https://avatars0.githubusercontent.com/u/13746731&#34;</span>}
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">GetUserInfo</span>()
}
</code></pre></div><p>开局就是一把问号，带着问题进行学习。请问 main 调用 <code>GetUserInfo</code> 后返回的 <code>&amp;User{...}</code>。这个变量是分配到栈上了呢，还是分配到堆上了？</p>
<h2 id="什么是堆栈">什么是堆/栈</h2>
<p>在这里并不打算详细介绍堆栈，仅简单介绍本文所需的基础知识。如下：</p>
<ul>
<li>堆（Heap）：一般来讲是人为手动进行管理，手动申请、分配、释放。一般所涉及的内存大小并不定，一般会存放较大的对象。另外其分配相对慢，涉及到的指令动作也相对多</li>
<li>栈（Stack）：由编译器进行管理，自动申请、分配、释放。一般不会太大，我们常见的函数参数（不同平台允许存放的数量不同），局部变量等等都会存放在栈上</li>
</ul>
<p>今天我们介绍的 Go 语言，它的堆栈分配是通过 Compiler 进行分析，GC 去管理的，而对其的分析选择动作就是今天探讨的重点</p>
<h2 id="什么是逃逸分析">什么是逃逸分析</h2>
<p>在编译程序优化理论中，逃逸分析是一种确定指针动态范围的方法，简单来说就是分析在程序的哪些地方可以访问到该指针</p>
<p>通俗地讲，逃逸分析就是确定一个变量要放堆上还是栈上，规则如下：</p>
<ol>
<li>是否有在其他地方（非局部）被引用。只要<strong>有可能</strong>被引用了，那么它<strong>一定</strong>分配到堆上。否则分配到栈上</li>
<li>即使没有被外部引用，但对象过大，无法存放在栈区上。依然有可能分配到堆上</li>
</ol>
<p>对此你可以理解为，逃逸分析是编译器用于决定变量分配到堆上还是栈上的一种行为</p>
<h2 id="在什么阶段确立逃逸">在什么阶段确立逃逸</h2>
<p>在编译阶段确立逃逸，注意并不是在运行时</p>
<h2 id="为什么需要逃逸">为什么需要逃逸</h2>
<p>这个问题我们可以反过来想，如果变量都分配到堆上了会出现什么事情？例如：</p>
<ul>
<li>垃圾回收（GC）的压力不断增大</li>
<li>申请、分配、回收内存的系统开销增大（相对于栈）</li>
<li>动态分配产生一定量的内存碎片</li>
</ul>
<p>其实总的来说，就是频繁申请、分配堆内存是有一定 “代价” 的。会影响应用程序运行的效率，间接影响到整体系统。因此 “按需分配” 最大限度的灵活利用资源，才是正确的治理之道。这就是为什么需要逃逸分析的原因，你觉得呢？</p>
<h2 id="怎么确定是否逃逸">怎么确定是否逃逸</h2>
<p>第一，通过编译器命令，就可以看到详细的逃逸分析过程。而指令集 <code>-gcflags</code> 用于将标识参数传递给 Go 编译器，涉及如下：</p>
<ul>
<li>
<p><code>-m</code> 会打印出逃逸分析的优化策略，实际上最多总共可以用 4 个 <code>-m</code>，但是信息量较大，一般用 1 个就可以了</p>
</li>
<li>
<p><code>-l</code> 会禁用函数内联，在这里禁用掉 inline 能更好的观察逃逸情况，减少干扰</p>
</li>
</ul>
<pre><code>$ go build -gcflags '-m -l' main.go
</code></pre><p>第二，通过反编译命令查看</p>
<pre><code>$ go tool compile -S main.go
</code></pre><p>注：可以通过 <code>go tool compile -help</code> 查看所有允许传递给编译器的标识参数</p>
<h2 id="逃逸案例">逃逸案例</h2>
<h3 id="案例一指针">案例一：指针</h3>
<p>第一个案例是一开始抛出的问题，现在你再看看，想想，如下：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">User</span> <span style="color:#66d9ef">struct</span> {
	<span style="color:#a6e22e">ID</span>     <span style="color:#66d9ef">int64</span>
	<span style="color:#a6e22e">Name</span>   <span style="color:#66d9ef">string</span>
	<span style="color:#a6e22e">Avatar</span> <span style="color:#66d9ef">string</span>
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GetUserInfo</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span> {
	<span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">User</span>{<span style="color:#a6e22e">ID</span>: <span style="color:#ae81ff">13746731</span>, <span style="color:#a6e22e">Name</span>: <span style="color:#e6db74">&#34;EDDYCJY&#34;</span>, <span style="color:#a6e22e">Avatar</span>: <span style="color:#e6db74">&#34;https://avatars0.githubusercontent.com/u/13746731&#34;</span>}
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">GetUserInfo</span>()
}
</code></pre></div><p>执行命令观察一下，如下：</p>
<pre><code>$ go build -gcflags '-m -l' main.go
# command-line-arguments
./main.go:10:54: &amp;User literal escapes to heap
</code></pre><p>通过查看分析结果，可得知 <code>&amp;User</code> 逃到了堆里，也就是分配到堆上了。这是不是有问题啊&hellip;再看看汇编代码确定一下，如下：</p>
<pre><code>$ go tool compile -S main.go
&quot;&quot;.GetUserInfo STEXT size=190 args=0x8 locals=0x18
	0x0000 00000 (main.go:9)	TEXT	&quot;&quot;.GetUserInfo(SB), $24-8
	...
	0x0028 00040 (main.go:10)	MOVQ	AX, (SP)
	0x002c 00044 (main.go:10)	CALL	runtime.newobject(SB)
	0x0031 00049 (main.go:10)	PCDATA	$2, $1
	0x0031 00049 (main.go:10)	MOVQ	8(SP), AX
	0x0036 00054 (main.go:10)	MOVQ	$13746731, (AX)
	0x003d 00061 (main.go:10)	MOVQ	$7, 16(AX)
	0x0045 00069 (main.go:10)	PCDATA	$2, $-2
	0x0045 00069 (main.go:10)	PCDATA	$0, $-2
	0x0045 00069 (main.go:10)	CMPL	runtime.writeBarrier(SB), $0
	0x004c 00076 (main.go:10)	JNE	156
	0x004e 00078 (main.go:10)	LEAQ	go.string.&quot;EDDYCJY&quot;(SB), CX
    ...
</code></pre><p>我们将目光集中到 CALL 指令，发现其执行了 <code>runtime.newobject</code> 方法，也就是确实是分配到了堆上。这是为什么呢？</p>
<h4 id="分析结果">分析结果</h4>
<p>这是因为 <code>GetUserInfo()</code> 返回的是指针对象，引用被返回到了方法之外了。因此编译器会把该对象分配到堆上，而不是栈上。否则方法结束之后，局部变量就被回收了，岂不是翻车。所以最终分配到堆上是理所当然的</p>
<h4 id="再想想">再想想</h4>
<p>那你可能会想，那就是所有指针对象，都应该在堆上？并不。如下：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">str</span> <span style="color:#f92672">:=</span> new(<span style="color:#66d9ef">string</span>)
	<span style="color:#f92672">*</span><span style="color:#a6e22e">str</span> = <span style="color:#e6db74">&#34;EDDYCJY&#34;</span>
}
</code></pre></div><p>你想想这个对象会分配到哪里？如下：</p>
<pre><code>$ go build -gcflags '-m -l' main.go
# command-line-arguments
./main.go:4:12: main new(string) does not escape
</code></pre><p>显然，该对象分配到栈上了。很核心的一点就是它有没有被作用域之外所引用，而这里作用域仍然保留在 <code>main</code> 中，因此它没有发生逃逸</p>
<h3 id="案例二未确定类型">案例二：未确定类型</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">str</span> <span style="color:#f92672">:=</span> new(<span style="color:#66d9ef">string</span>)
	<span style="color:#f92672">*</span><span style="color:#a6e22e">str</span> = <span style="color:#e6db74">&#34;EDDYCJY&#34;</span>

	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">str</span>)
}
</code></pre></div><p>执行命令观察一下，如下：</p>
<pre><code>$ go build -gcflags '-m -l' main.go
# command-line-arguments
./main.go:9:13: str escapes to heap
./main.go:6:12: new(string) escapes to heap
./main.go:9:13: main ... argument does not escape
</code></pre><p>通过查看分析结果，可得知 <code>str</code> 变量逃到了堆上，也就是该对象在堆上分配。但上个案例时它还在栈上，我们也就 <code>fmt</code> 输出了它而已。这&hellip;到底发生了什么事？</p>
<h4 id="分析结果-1">分析结果</h4>
<p>相对案例一，案例二只加了一行代码 <code>fmt.Println(str)</code>，问题肯定出在它身上。其原型：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">a</span> <span style="color:#f92672">...</span><span style="color:#66d9ef">interface</span>{}) (<span style="color:#a6e22e">n</span> <span style="color:#66d9ef">int</span>, <span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>)
</code></pre></div><p>通过对其分析，可得知当形参为 <code>interface</code> 类型时，在编译阶段编译器无法确定其具体的类型。因此会产生逃逸，最终分配到堆上</p>
<p>如果你有兴趣追源码的话，可以看下内部的 <code>reflect.TypeOf(arg).Kind()</code> 语句，其会造成堆逃逸，而表象就是 <code>interface</code> 类型会导致该对象分配到堆上</p>
<h3 id="案例三泄露参数">案例三、泄露参数</h3>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">User</span> <span style="color:#66d9ef">struct</span> {
	<span style="color:#a6e22e">ID</span>     <span style="color:#66d9ef">int64</span>
	<span style="color:#a6e22e">Name</span>   <span style="color:#66d9ef">string</span>
	<span style="color:#a6e22e">Avatar</span> <span style="color:#66d9ef">string</span>
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GetUserInfo</span>(<span style="color:#a6e22e">u</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span> {
	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">u</span>
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">GetUserInfo</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">User</span>{<span style="color:#a6e22e">ID</span>: <span style="color:#ae81ff">13746731</span>, <span style="color:#a6e22e">Name</span>: <span style="color:#e6db74">&#34;EDDYCJY&#34;</span>, <span style="color:#a6e22e">Avatar</span>: <span style="color:#e6db74">&#34;https://avatars0.githubusercontent.com/u/13746731&#34;</span>})
}
</code></pre></div><p>执行命令观察一下，如下：</p>
<pre><code>$ go build -gcflags '-m -l' main.go
# command-line-arguments
./main.go:9:18: leaking param: u to result ~r1 level=0
./main.go:14:63: main &amp;User literal does not escape
</code></pre><p>我们注意到 <code>leaking param</code> 的表述，它说明了变量 <code>u</code> 是一个泄露参数。结合代码可得知其传给 <code>GetUserInfo</code> 方法后，没有做任何引用之类的涉及变量的动作，直接就把这个变量返回出去了。因此这个变量实际上并没有逃逸，它的作用域还在 <code>main()</code> 之中，所以分配在栈上</p>
<h4 id="再想想-1">再想想</h4>
<p>那你再想想怎么样才能让它分配到堆上？结合案例一，举一反三。修改如下：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">User</span> <span style="color:#66d9ef">struct</span> {
	<span style="color:#a6e22e">ID</span>     <span style="color:#66d9ef">int64</span>
	<span style="color:#a6e22e">Name</span>   <span style="color:#66d9ef">string</span>
	<span style="color:#a6e22e">Avatar</span> <span style="color:#66d9ef">string</span>
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GetUserInfo</span>(<span style="color:#a6e22e">u</span> <span style="color:#a6e22e">User</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span> {
	<span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">u</span>
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">GetUserInfo</span>(<span style="color:#a6e22e">User</span>{<span style="color:#a6e22e">ID</span>: <span style="color:#ae81ff">13746731</span>, <span style="color:#a6e22e">Name</span>: <span style="color:#e6db74">&#34;EDDYCJY&#34;</span>, <span style="color:#a6e22e">Avatar</span>: <span style="color:#e6db74">&#34;https://avatars0.githubusercontent.com/u/13746731&#34;</span>})
}
</code></pre></div><p>执行命令观察一下，如下：</p>
<pre><code>$ go build -gcflags '-m -l' main.go
# command-line-arguments
./main.go:10:9: &amp;u escapes to heap
./main.go:9:18: moved to heap: u
</code></pre><p>只要一小改，它就考虑会被外部所引用，因此妥妥的分配到堆上了</p>
<h2 id="总结">总结</h2>
<p>在本文我给你介绍了逃逸分析的概念和规则，并列举了一些例子加深理解。但实际肯定远远不止这些案例，你需要做到的是掌握方法，遇到再看就好了。除此之外你还需要注意：</p>
<ul>
<li>静态分配到栈上，性能一定比动态分配到堆上好</li>
<li>底层分配到堆，还是栈。实际上对你来说是透明的，不需要过度关心</li>
<li>每个 Go 版本的逃逸分析都会有所不同（会改变，会优化）</li>
<li>直接通过 <code>go build -gcflags '-m -l'</code> 就可以看到逃逸分析的过程和结果</li>
<li>到处都用指针传递并不一定是最好的，要用对</li>
</ul>
<p>之前就有想过要不要写 “逃逸分析” 相关的文章，直到最近看到在夜读里有人问，还是有写的必要。对于这块的知识点。我的建议是适当了解，但没必要硬记。靠基础知识点加命令调试观察就好了。像是曹大之前讲的 “你琢磨半天逃逸分析，一压测，瓶颈在锁上”，完全没必要过度在意&hellip;</p>
<h2 id="参考">参考</h2>
<ul>
<li><a href="http://www.agardner.me/golang/garbage/collection/gc/escape/analysis/2015/10/18/go-escape-analysis.html">Golang escape analysis</a></li>
<li><a href="https://golang.org/doc/faq#stack_or_heap">FAQ</a></li>
<li><a href="https://zh.wikipedia.org/wiki/%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90">逃逸分析</a></li>
</ul>


                                        
                                        
                                        
                                        <div class="rp4wp-related-posts">
                                            <h3>相关文章</h3>
                                            <ul>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/go/map/2019-04-07-why-map-no-order/">为什么遍历 Go map 是无序的</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/go/talk/2019-03-31-go-ins/">简单围观一下有趣的 //go: 指令</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/go/map/2019-03-24-map-assign/">深入理解 Go map：赋值和扩容迁移</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/go/map/2019-03-05-map-access/">深入理解 Go map：初始化和访问元素</a>
                                                    </div>
                                                </li>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/go/talk/2019-02-17-for-loop-json-unmarshal/">for-loop 与 json.Unmarshal 性能分析概要</a>
                                                    </div>
                                                </li>
                                                
                                            </ul>
                                        </div>
                                        
                                        
                                    </div>

                                    
                                    
                                    

                                    
                                    <div class="post-meta">
                                        
                                        
                                        <div class="post-tags">
                                            <ul>
                                            
                                            <li>
                                                <a href="/tags/go" title="View all posts tagged match">go</a>
                                            </li>
                                            
                                            </ul>
                                        </div>
                                        
                                        
                                        <nav class="further-reading">
                                            
                                            <div class="previous">
                                                <span>&lt;&lt; Prev</span>
                                                <a href="https://www.lican.site/posts/go/map/2019-04-07-why-map-no-order/"
                                                    rel="prev">为什么遍历 Go map 是无序的</a> </div>
                                            
                                            
                                            <div class="next">
                                                <span>Next >></span>
                                                <a href="https://www.lican.site/posts/go/panic/2019-05-21-panic-and-recover/">深入理解 Go panic and recover</a> 
                                            </div>
                                            
                                        </nav>
                                    </div>
                                    
                                    

                                    
                                    
                                    

                                </div>
                            </article>
                        </div>
                    </div>
                </section>
                <aside class="sidebar sidebar-primary" id="sidebar-primary" role="complementary">
    <h1 class="screen-reader-text">Sidebar</h1>
    

    
    
    <section id="text-2" class="widget widget_text">
        <div class="textwidget">
            
            <div id="profile">
                <div id="profile_picture"><img src="https://www.lican.site/logo/logo.png"></div>
                <div id="profile_intro">
                    <p><span class="name">Lican</span></p>
                    <p class="intro">全栈开发者，爱好造轮子。</p>
                </div>
            </div>
            
            <p>
                <script type="text/javascript">
                    (function ($) {
                        $(document).ready(function () {
                            var menuPrimaryContainer = $('#menu-primary-container');
                            var profile = $('#text-2');
                            $('#toggle-navigation').click(function () {
                                if (menuPrimaryContainer.hasClass('open')) {
                                    profile.removeClass('open');
                                } else {
                                    profile.addClass('open');
                                }
                            });
                        });
                    })(jQuery);
                </script>
            </p>
        </div>
    </section>
    
    
    
    
    
    <section id="text-5" class="widget widget_text">
        <h2 class="widget-title">开源项目</h2>
        <div class="textwidget">
            <div id="projects" style="line-height: 22px;">
                
                <a href="https://github.com/idoubi/gonews"
                    target="_blank">gonews</a>: &nbsp;Daily news for golang<br>
                
                <a href="https://github.com/idoubi/sql2struct"
                    target="_blank">sql2struct</a>: &nbsp;Generate go struct according to SQL<br>
                
                <a href="https://github.com/idoubi/goz"
                    target="_blank">goz</a>: &nbsp;Request library used in golang<br>
                
        </div>
    </section>
    
    

    
    
    
    
    

    
    
    

    
    
    

    
    
    
    
</aside>
            </div>
        </div>

        <footer id="site-footer" class="site-footer" role="contentinfo">
    <div class="max-width">
    </div>
    <div class="footer">
        <div id="footercontent">
            © lican.vip All rights reserved<br/>
            Built with Hugo Theme <a href="https://github.com/idoubi/hugo-theme-period" target="_blank">Period</a>
        </div>
    </div>
</footer>

<script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?e8351b6d4626d5881d439ea1f6184baa";
      var s = document.getElementsByTagName("script")[0]; 
      s.parentNode.insertBefore(hm, s);
    })();
</script>
    
    
    </div>
    
</body>

</html>